package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWork;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wzh
 * @since 2022-5-17
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Autowired
    private ISeckillVoucherService seckillVoucherService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisIdWork redisIdWork;

    @Autowired
    private RedissonClient redissonClient;

    private IVoucherOrderService o;

    //    使用静态代码块加载脚本
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

//    private BlockingQueue<VoucherOrder> orderTasks=new ArrayBlockingQueue<>(1024*1024);//创建阻塞队列

    private static final ExecutorService SECKILL_ORDER_EXCUTOR = Executors.newSingleThreadExecutor();//创建一个线程池

    @PostConstruct//代表的含义为当前类初始化后来执行
    private void init() {
        SECKILL_ORDER_EXCUTOR.submit(new VoucherOrderHandler());
    }

    private class VoucherOrderHandler implements Runnable {//内部类
        String queueName = "stream.orders";

        @Override
        public void run() {
            while (true) {
                try {
//          1.获取消息队列中的订单信息 xreadgroup
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create(queueName, ReadOffset.lastConsumed())//lastConsumed 最近一次未消费的消息
                    );
//                2.判断消息是否获取成功
                    if (list == null || list.isEmpty()) {
                        //                2.1 如果获取失败则说明没有消息,进行下一次循环
                        continue;
                    }
//                2.2 如果获取成功，则进行下单
//                解析消息中的订单
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> values = record.getValue();//将map转为order对象
                    VoucherOrder voucherOrder1 = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
                    handleVoucherOrder(voucherOrder1);
//                3.ack确认  SACK stream.orders g1 id
                    stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
                } catch (Exception e) {
                    log.error("处理订单异常", e);
                    handlePendingList();
                }

//          2.创建订单
            }
        }

        private void handlePendingList() {
            while (true) {
                try {
//          1.获取pending-list中的订单信息 xreadgroup
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(queueName, ReadOffset.from("0"))//lastConsumed 最近一次未消费的消息
                    );
//                2.判断消息是否获取成功
                    if (list == null || list.isEmpty()) {
                        //                2.1 如果获取失败则说明pending-list中没有消息,退出循环
                        break;
                    }
//                2.2 如果获取成功，则进行下单
//                解析消息中的订单
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> values = record.getValue();//将map转为order对象
                    VoucherOrder voucherOrder1 = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
                    handleVoucherOrder(voucherOrder1);
//                3.ack确认  SACK stream.orders g1 id
                    stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
                } catch (Exception e) {
                    log.error("处理订单异常", e);
                    try {
                        Thread.sleep(20);//降低执行频率
                    } catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                }

            }
        }
    }


//    private class VoucherOrderHandler implements Runnable{//内部类
//
//        @Override
//        public void run() {
//      while (true){
////          1.获取队列中的订单信息
//          try {
//              VoucherOrder voucherOrder = orderTasks.take();
////              创建订单
//              handleVoucherOrder(voucherOrder);
//
//          } catch (InterruptedException e) {
//            log.error("处理订单异常",e);
//          }
//
////          2.创建订单
//      }
//        }
//    }

    private void handleVoucherOrder(VoucherOrder voucherOrder) {
        Long userId = voucherOrder.getUserId();
        Long voucherId = voucherOrder.getVoucherId();
        RLock lock = redissonClient.getLock("lock:order:" + userId);

        boolean b = lock.tryLock();
        if (!b) {
            log.error("不可以重下单");
            return;
        }
        try {
            o.createVoucherOrder(voucherOrder);
        } finally {
//            释放锁
            lock.unlock();
        }

    }

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    @Override
    public Result seckillVoucher(Long voucherId) {
//        获取用户
        Long id = UserHolder.getUser().getId();

//        获取订单id
        Long id1 = redisIdWork.nextId("order");
//        1.执行脚本,判断购买资格，发送消息到我们的消息队列
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), id.toString(), String.valueOf(id1)
        );
        int i = result.intValue();

//        2.判断结果是为0
        if (i != 0) {
            return Result.fail(i == 1 ? "库存不足" : "不能重复下单");
        }

//        2.1 不为0则代表没有购买资格

//        2.2 为0，有购买资格，将下单信息保存到阻塞队列
        VoucherOrder voucherOrder = new VoucherOrder();
        voucherOrder.setId(id1);
//        用户id
        voucherOrder.setUserId(id);
//        代金劵id
        voucherOrder.setVoucherId(voucherId);
//        获取代理对象
        o = (IVoucherOrderService) AopContext.currentProxy();//拿到该类的代理对象
//        3 返回订单id
        return Result.ok(id1);
    }

//    @Override
//    public Result seckillVoucher(Long voucherId) {
////        1.查询优惠劵
//        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
////        2.判断秒杀是否已经开始
//        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
////            尚未开始
//            return Result.fail("秒杀尚未开始");
//
//        }
////        3.判断秒杀是否已经结束
//        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
////            已经结束
//            return Result.fail("订单秒杀已经结束");
//        }
////        4.判断库存是否充足
//        if (voucher.getStock() < 1) {
////            库存不够
//            return Result.fail("抢购失败，库存已不足");
//        }
//        Long id = UserHolder.getUser().getId();
//        //synchronized(id.toString().intern()) {//给用户的id上同步锁,当用户id的值相同时就会被锁定，在事务提交后才能释放锁，悲观锁
////            获取代理对象，确保事务可以生效
////        创建锁对象
//        SimpleRedisLock simpleRedisLock=new SimpleRedisLock(stringRedisTemplate,"order:"+id);
////        获取锁
//        boolean b = simpleRedisLock.tryLock(1200);
//        if (!b) {
//            return Result.fail("优惠券不可重复购买");
//        }
//
//        ISeckillVoucherService o =(ISeckillVoucherService) AopContext.currentProxy();//拿到该类的代理对象
//            return createVoucherOrder(voucherId);
//       // }
//
//    }


    @Transactional//加上事务，一旦出现问题可以及时回滚
    @Override
    public void createVoucherOrder(VoucherOrder voucherOrder) {
        //        一人一单
        Long id = voucherOrder.getUserId();
        Integer count = query().eq("user_id", id).eq("voucher_id", voucherOrder.getVoucherId()).count();
        if (count > 0) {
//                return Result.fail("用户已经购买过一次");
            log.error("用户已经购买过一次");
        }
        //        5.扣除库存
        boolean success = seckillVoucherService.update()
                .setSql("stock=stock-1")
                .eq("voucher_id", voucherOrder.getVoucherId()).update();
        if (!success) {
//                return Result.fail("库存不足!");
            log.error("库存不足!");
        }

//        6.生成订单
//            VoucherOrder voucherOrder = new VoucherOrder();
//        订单id
//            Long orderid = redisIdWork.nextId("order");
//            voucherOrder.setId(orderid);
////        用户id
//            Long userid = UserHolder.getUser().getId();
//            voucherOrder.setUserId(userid);
////        代金劵id
//            voucherOrder.setVoucherId(voucherId);

        boolean save = this.save(voucherOrder);
////        返回订单id
//            return Result.ok(orderid);

    }
}

